// Deliver State, for managing incoming mail requests (from any source) and
// delivering them to a local file system.

package delivers

import (
  "path"
  "os"
)

/**
 * Tiny set implementation backed on top of map[string]. Supports .Contains
 * and .Put.
 */
type StringSet map[string] interface{}

func (s StringSet) Contains(v string) bool {
  _, exists := s[v]
  return exists
}

func (s StringSet) Put(v string) {
  s[v] = nil
}

// Error code constants.
const (
  NoError = iota
  InvalidFormat
  AlreadySpecified
  NotHere
)

type State interface {
  From(address string) int
  GetFrom() *string
  To(address string) int
  GetTo() StringSet
}

type _State struct {
  basepath string
  localusers bool
  hostname string
  from *string
  to StringSet
}

func NewState(basepath string, localusers bool, hostname string) State {
  return &_State{basepath, localusers, hostname, nil, make(StringSet)}
}

func (s *_State) From(address string) int {
  if s.from != nil {
    return AlreadySpecified
  }

  s.from = &address
  return NoError
}

func (s *_State) GetFrom() *string {
  return s.from
}

func (s *_State) To(address string) int {
  if s.to.Contains(address) {
    return AlreadySpecified
  }

  // path.Match allows us to match a specific type of path; in this case it
  // sanity-checks an address so it can't escape a path (e.g. prevents an
  // address of "/etc/passwd").
  matched, _ := path.Match("*", address)
  if !matched {
    return InvalidFormat
  }

  // Search for the address inside basepath folder.
  path := path.Join(s.basepath + "/", address)
  _, err := os.Stat(path)
  if err != nil {
    return NotHere
  }

  // TODO: local address lookup

  s.to.Put(address)
  return NoError
}

func (s *_State) GetTo() StringSet {
  // It would be cool if this was an immutable view of the set. Perhaps that's
  // a bit too serious and my head is still in Java-land.
  return s.to
}

